package com.hl.godsent.utils

import android.content.Context
import android.util.Base64
import android.util.Log
import com.hl.godsent.BuildConfig
import com.blankj.utilcode.util.FileUtils
import com.blankj.utilcode.util.PathUtils
import java.io.*
import java.text.SimpleDateFormat
import java.util.*


import java.util.concurrent.Executors

/**
 * 用来打日志，在Release状态下会把必要打日志打印到文件中，备用。
 */
object  CoreLog {
    private const val TAG = "CoreLog"
    const val LOG_TO_FILE_LEVEL = Log.INFO // 日志等级，大于这个日志等级的才会在生产环境中打印到日志文件中
    const val MAX_LOG_EXPIRE_DAYS = 7 // 日志最多保存几天
    private const val FORCE_LOG_TO_FILE = false // 如果打开了 无论在DEBUG模式下还是在release模式下都会把日志写入到文件中
    private const val LOG_FILE_DIR = "logs/"
    private var logFileLevel // 写入Log日志的日志级别
            = 0
    private const val MIN_LOG_LEVEL = Log.VERBOSE
    private val logTimeFormatter = SimpleDateFormat("yyyy-MM-dd HH-mm-ss", Locale.CHINA)
    private val logDateFormatter = SimpleDateFormat("yyyy-MM-dd", Locale.CHINA)
    private var logWriter: FileWriter? = null
    private var logFilePath: String? = null
    private const val logFileLock = true
    private val executors = Executors.newSingleThreadExecutor()

    /**
     *
     * @param logLevel {code Log.VERBOSE} etc.
     */
    fun setLogLevel(logLevel: Int) {
        logFileLevel = logLevel
    }

    private fun tryToDeleteOutDateLogFiles(logFileDir: String, todayDateString: String) {
        try {
            val logFiles = File(logFileDir).listFiles()
            val todayDate = logDateFormatter.parse(todayDateString)
            if (logFiles == null || todayDate == null) {
                return
            }
            for (file in logFiles) {
                if (file != null) {
                    val fileName = file.name.substring(0, file.name.lastIndexOf("."))
                    val destDate = logDateFormatter.parse(fileName) ?: continue
                    if ((todayDate.time - destDate.time) / 1000L / 3600L / 24L >= MAX_LOG_EXPIRE_DAYS) {
                        file.delete()
                    }
                }
            }
        } catch (e: Exception) {
            e(TAG, "Failed to delete out-date log files")
        }
    }

    fun v(tag: String?, logMessage: String) {
        writeLog(Log.VERBOSE, tag, logMessage, null)
    }

    fun v(tag: String?, logMessage: String, logVerbose: Boolean) {
        if (logVerbose) writeLog(Log.VERBOSE, tag, logMessage, null)
    }

    @JvmOverloads
    fun d(tag: String?, logMessage: String, throwable: Throwable? = null) {
        writeLog(Log.DEBUG, tag, logMessage, throwable)
    }

    fun i(tag: String?, logMessage: String) {
        writeLog(Log.INFO, tag, logMessage, null)
    }

    @JvmOverloads
    fun w(tag: String?, logMessage: String, throwable: Throwable? = null) {
        writeLog(Log.WARN, tag, logMessage, throwable)
    }

    @JvmOverloads
    fun e(tag: String?, logMessage: String, throwable: Throwable? = null) {
        writeLog(Log.ERROR, tag, logMessage, throwable)
    }

    fun writeLog(logLevel: Int, tag: String?, logMessage: String, throwable: Throwable?) {
        var logMessage = logMessage
        if (throwable != null) {
            logMessage = """$logMessage
${Log.getStackTraceString(throwable)}"""
        }
        if (BuildConfig.DEBUG && logLevel >= MIN_LOG_LEVEL) {
            Log.println(logLevel, tag, logMessage)
        }
        if ((!BuildConfig.DEBUG || FORCE_LOG_TO_FILE) && logLevel >= logFileLevel) {
            val sb = StringBuilder()
            sb.append(logTimeFormatter.format(Date())).append(" ")
                .append(levelToString(logLevel))
                .append(" ")
                .append(tag)
                .append(" ")
                .append(logMessage)
            executors.submit {
                try {
                    synchronized(logFileLock) {
                        val encrypted: ByteArray? = EncryptUtil.encryptV2(sb.toString().toByteArray())
//                            EncryptUtil.encryptV2(sb.toString().toByteArray())
                        val encryptedString: String =
                            Base64.encodeToString(
                                encrypted,
                                Base64.DEFAULT
                            )
                        logWriter!!.append(encryptedString)
                        logWriter!!.flush()
                    }
                } catch (e: IOException) {
                    Log.e(TAG, "Failed to write log to file", e)
                }
            }
        }
    }

    private fun readLog(): String {
        return if (!BuildConfig.DEBUG || FORCE_LOG_TO_FILE) {
            val resultStringBuilder = StringBuilder()
            try {
                FileReader(logFilePath).use { fileReader ->
                    BufferedReader(fileReader).use { bf ->
                        while (true) {
                            val line: String? = bf.readLine()
                            if (line != null) {
                                resultStringBuilder.append(line).append("\n")
                            } else {
                                break
                            }
                        }
                    }
                }
            } catch (e: Exception) {
                return "Read log failed:" + Arrays.toString(e.stackTrace)
            }
            resultStringBuilder.toString()
        } else {
            ""
        }
    }

    fun reportTodayLogToOSS(context: Context?, userId: String?): Boolean {
//        TODO: 上报OBS
//        return OSSFastUploadUtil.uploadLog(context, userId, readLog())
        return true
    }

    val logSize: Long
        get() = if (!BuildConfig.DEBUG || FORCE_LOG_TO_FILE) {
            val file = File(logFilePath)
            file.length()
        } else {
            0
        }

    private fun levelToString(logLevel: Int): String {
        return when (logLevel) {
            Log.VERBOSE -> "V"
            Log.DEBUG -> "D"
            Log.INFO -> "I"
            Log.WARN -> "WW⚠️️"
            Log.ERROR -> "EEE‼️"
            else -> "U"
        }
    }

    init {
        if (!BuildConfig.DEBUG || FORCE_LOG_TO_FILE) {
            logFileLevel = if (BuildConfig.DEBUG) Log.VERBOSE else LOG_TO_FILE_LEVEL
            val today = logDateFormatter.format(Date())
            val logFileDir = PathUtils.getExternalAppDataPath() + "/" + LOG_FILE_DIR
            logFilePath = "$logFileDir$today.log"
            FileUtils.createOrExistsDir(logFileDir)
            try {
                logWriter = FileWriter(logFilePath, true)
            } catch (e: IOException) {
                e.printStackTrace()
            }
            tryToDeleteOutDateLogFiles(
                logFileDir,
               today
            )
        } else {
            logFilePath = null
        }
    }
}